package com.linkoog.devtools.intellij.ui.button;

import com.intellij.ide.HelpTooltip;
import com.intellij.ide.ui.laf.darcula.DarculaUIUtil;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.ShortcutSet;
import com.intellij.openapi.actionSystem.ex.ActionButtonLook;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.ui.ExperimentalUI;
import com.intellij.util.ui.BaseButtonBehavior;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.update.Activatable;
import com.intellij.util.ui.update.UiNotifyConnector;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;

public class InlineIconButton extends JComponent {
    private ActionListener actionListener;
    private Icon icon;
    private Icon hoveredIcon;
    private Icon disabledIcon;
    private String tooltip;
    private ShortcutSet shortcut;
    private boolean withBackgroundHover;

    public InlineIconButton(Icon icon, Icon hoveredIcon, Icon disabledIcon, String tooltip, ShortcutSet shortcut) {
        this.icon = icon;
        this.hoveredIcon = hoveredIcon;
        this.disabledIcon = disabledIcon;
        this.tooltip = tooltip;
        this.shortcut = shortcut;
        this.withBackgroundHover = false;
        setUI(new InlineIconButtonUI());
    }

    public ActionListener getActionListener() {
        return actionListener;
    }

    public void setActionListener(ActionListener actionListener) {
        this.actionListener = actionListener;
    }

    public Icon getIcon() {
        return icon;
    }

    public void setIcon(Icon icon) {
        this.icon = icon;
    }

    public Icon getHoveredIcon() {
        return hoveredIcon;
    }

    public void setHoveredIcon(Icon hoveredIcon) {
        this.hoveredIcon = hoveredIcon;
    }

    public Icon getDisabledIcon() {
        return disabledIcon;
    }

    public void setDisabledIcon(Icon disabledIcon) {
        this.disabledIcon = disabledIcon;
    }

    public String getTooltip() {
        return tooltip;
    }

    public void setTooltip(String tooltip) {
        this.tooltip = tooltip;
    }

    public ShortcutSet getShortcut() {
        return shortcut;
    }

    public void setShortcut(ShortcutSet shortcut) {
        this.shortcut = shortcut;
    }

    public boolean isWithBackgroundHover() {
        return withBackgroundHover;
    }

    public void setWithBackgroundHover(boolean withBackgroundHover) {
        this.withBackgroundHover = withBackgroundHover;
    }

    private class InlineIconButtonUI extends ComponentUI {
        private BaseButtonBehavior buttonBehavior;
        private UiNotifyConnector tooltipConnector;
        private KeyListener spaceKeyListener;
        private PropertyChangeListener propertyListener;

        @Override
        public void paint(Graphics g, JComponent c) {
            BaseButtonBehavior behavior = buttonBehavior;
            if (behavior == null) {
                return;
            }

            InlineIconButton button = (InlineIconButton) c;
            Icon currentIcon = getCurrentIcon(button);

            Graphics2D g2 = (Graphics2D) g.create();
            try {
                ActionButtonLook look = button.isWithBackgroundHover() ? ActionButtonLook.SYSTEM_LOOK : ActionButtonLook.INPLACE_LOOK;
                int buttonState = ActionButtonLook.getButtonState(button.isEnabled(), behavior.isHovered(), behavior.isFocused(), behavior.isPressedByMouse(), behavior.isPressedByKeyboard());
                if (button.isEnabled() || !StartupUiUtil.INSTANCE.isDarkTheme() || ExperimentalUI.isNewUI()) {
                    look.paintBackground(g2, button, buttonState);
                }
                if (behavior.isFocused()) {
                    Rectangle rect = new Rectangle(button.getSize());
                    DarculaUIUtil.paintFocusBorder(g2, rect.width, rect.height, 0f, true);
                } else {
                    look.paintBorder(g2, button, buttonState);
                }

                Point iconPoint = getIconPosition(button, currentIcon);
                currentIcon.paintIcon(button, g2, iconPoint.x, iconPoint.y);
            } finally {
                g2.dispose();
            }
        }

        @Override
        public Dimension getMinimumSize(JComponent c) {
            return getPreferredSize(c);
        }

        @Override
        public Dimension getPreferredSize(JComponent c) {
            InlineIconButton button = (InlineIconButton) c;
            Icon currentIcon = getCurrentIcon(button);
            Dimension size;
            if (button.isWithBackgroundHover()) {
                size = calculateComponentSizeWithBackground(currentIcon);
            } else {
                size = new Dimension(currentIcon.getIconWidth(), currentIcon.getIconHeight());
            }

            JBInsets.addTo(size, button.getInsets());
            return size;
        }

        @Override
        public Dimension getMaximumSize(JComponent c) {
            return getPreferredSize(c);
        }

        private Icon getCurrentIcon(InlineIconButton button) {
            if (!button.isEnabled()) {
                return button.getDisabledIcon() != null ? button.getDisabledIcon() : IconLoader.getDisabledIcon(button.getIcon());
            }
            if (buttonBehavior != null && (buttonBehavior.isHovered() || button.hasFocus())) {
                return button.getHoveredIcon() != null ? button.getHoveredIcon() : button.getIcon();
            }
            return button.getIcon();
        }

        @Override
        public void installUI(JComponent c) {
            InlineIconButton button = (InlineIconButton) c;
            buttonBehavior = new BaseButtonBehavior(button, (Void) null) {

                @Override
                protected void execute(MouseEvent e) {
                    if (button.isEnabled()) {
                        ActionListener listener = button.getActionListener();
                        if (listener != null) {
                            listener.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, "execute", e.getModifiers()));
                        }
                    }
                }
            };
            buttonBehavior.setupListeners();
            spaceKeyListener = new KeyAdapter() {
                @Override
                public void keyReleased(KeyEvent e) {
                    if (button.isEnabled() && !e.isConsumed() && e.getModifiers() == 0 && e.getKeyCode() == KeyEvent.VK_SPACE) {
                        ActionListener listener = button.getActionListener();
                        if (listener != null) {
                            listener.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, "execute", e.getModifiers()));
                        }
                        e.consume();
                    }
                }
            };
            button.addKeyListener(spaceKeyListener);

            Activatable tooltipActivatable = new Activatable() {
                @Override
                public void showNotify() {
                    String tooltip = button.getTooltip();
                    if (tooltip != null) {
                        HelpTooltip helpTooltip = new HelpTooltip();
                        helpTooltip.setTitle(tooltip);
                        helpTooltip.setShortcut(button.getShortcut() != null ? KeymapUtil.getFirstKeyboardShortcutText(button.getShortcut()) : null);
                        helpTooltip.installOn(button);
                    }
                }

                @Override
                public void hideNotify() {
                    HelpTooltip.dispose(button);
                }
            };
            tooltipConnector = UiNotifyConnector.installOn(button, tooltipActivatable);

            propertyListener = evt -> {
                HelpTooltip tooltip = HelpTooltip.getTooltipFor(button);
                if (tooltip != null) {
                    tooltip.setTitle(button.getTooltip());
                }
                button.revalidate();
                button.repaint();
            };
            button.addPropertyChangeListener(propertyListener);

            button.setOpaque(false);
            button.setFocusable(true);
            button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        }

        @Override
        public void uninstallUI(JComponent c) {
            if (tooltipConnector != null) {
                Disposer.dispose(tooltipConnector);
            }
            tooltipConnector = null;
            if (spaceKeyListener != null) {
                c.removeKeyListener(spaceKeyListener);
            }
            spaceKeyListener = null;
            if (propertyListener != null) {
                c.removePropertyChangeListener(propertyListener);
            }
            propertyListener = null;
            buttonBehavior = null;
            HelpTooltip.dispose(c);
        }

        private Point getIconPosition(JComponent component, Icon icon) {
            Rectangle rect = new Rectangle(component.getWidth(), component.getHeight());
            Insets i = component.getInsets();
            JBInsets.removeFrom(rect, i);

            int x = i.left + (rect.width - icon.getIconWidth()) / 2;
            int y = i.top + (rect.height - icon.getIconHeight()) / 2;

            return new Point(x, y);
        }

        private Dimension calculateComponentSizeWithBackground(Icon icon) {
            int width = Math.max(ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE.width, icon.getIconWidth());
            int height = Math.max(ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE.height, icon.getIconHeight());
            return new Dimension(width, height);
        }
    }
}
